/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.database.metamodel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.tree.TreeNode;


/**
 * BaseComponent provides the heirachical data structure that is observed in
 * most of Workflow components at both runtime and design time.  This is an
 * internal class that should not be used directly by application developers.
 */
public class TreeNodeBase implements TreeNode {
    private ArrayList children = new ArrayList();
    private Object viewRef;

    /**
     * A convenient method for Id
     *
     * @return name : String component name.
     */
    private String name;
    private TreeNodeBase parent;

    public TreeNodeBase() {
        super();
    }

    /**
     * Constructor that takes in a component id.
     *
     * @param name : String component id.
     */
    protected TreeNodeBase(String name) {
        this.name = name;
    }

    public boolean getAllowsChildren() {
        return true;
    }

    public TreeNode getChildAt(int childIndex) {
        return (TreeNode) getChildren().get(childIndex);
    }

    /**
     * Return a list of child components with the same type
     *
     * @param type : Class Type of component interested in
     * @param name : Name of the component.
     *
     * @return list of children of the given class, empty list will be returned
     *         if found none.
     */
    public TreeNodeBase getChildByTypeAndName(Class type, String name) {
        if ((name == null) || (name.length() == 0)) {
            throw new IllegalArgumentException("BaseComponent.getChildByTypeAndName.Name.Required");
        }

        Iterator iter = getChildren().iterator();

        while (iter.hasNext()) {
            TreeNodeBase bc = (TreeNodeBase) iter.next();

            if (type.isInstance(bc) && name.equals(bc.getName())) {
                return bc;
            }
        }

        return null; //subclass should override this, and load from persistent source 
    }

    public int getChildCount() {
        return getChildren().size();
    }

    /**
     * Returns all the children. We need to be clear if we  need to be in synch
     * with persistent storage. Same for all other get list methods.
     *
     * @return children : List of child components.
     */
    public List getChildren() {
        return children;
    }

    /**
     * Return a list of child components with the same name
     *
     * @param name : String name of component interested in
     *
     * @return list of children of the given name, empty list will be returned if
     *         found none.
    */
    public List getChildrenByName(String name) //this would not be very useful
     {
        if ((name == null) || (name.length() == 0)) {
            throw new IllegalArgumentException("BaseComponent.getChildByTypeAndName.Name.Required");
        }

        Iterator c = children.iterator();
        Vector v = new Vector();

        while (c.hasNext()) {
            TreeNodeBase child = (TreeNodeBase) c.next();

            if ((child != null) && child.getName().equals(name.toString())) {
                v.add(child);
            }
        }

        return v;
    }

    /**
     * Return a list of child components with the same type
     *
     * @param type : Class Type of component interested in
     *
     * @return list of children of the given class, empty list will be returned
     *         if found none.
     */
    public List getChildrenByType(Class type) {
        Vector list = new Vector();
        Iterator iter = getChildren().iterator();

        while (iter.hasNext()) {
            TreeNodeBase bc = (TreeNodeBase) iter.next();

            if (type.isInstance(bc)) {
                list.addElement(bc);
            }
        }

        return list;
    }

    public int getIndex(TreeNode node) {
        return getChildren().indexOf(node);
    }

    public boolean isLeaf() {
        return false;
    }

    public void setName(String newName) {
        name = newName;
    }

    public String getName() {
        return name;
    }

    public TreeNode getParent() {
        return parent;
    }

    /**
     * Return parent of this object.
     *
     * @return parent : BaseComponent
     */
    public TreeNodeBase getParentComponent() {
        return parent;
    }

    public List getSiblingByType(Class type) {
        if (getParentComponent() == null) {
            return Collections.EMPTY_LIST;
        }

        return getParentComponent().getChildrenByType(type);
    }

    public void setView(Object pViewRef) {
        viewRef = pViewRef;
    }

    public Object getView() {
        return viewRef;
    }

    /**
     * Add a child to this component, will set parent to be this component.
     *
     * @param child : BaseComponent child to be added.
     */
    public void addChild(TreeNodeBase pChild) {
        if (pChild.parent != null) {
            pChild.parent.removeChild(pChild);
        }

        synchronized (children) {
            children.add(pChild);
        }

        pChild.setParent(this);
    }

    //tree node implementation
    public Enumeration children() {
        Vector v = new Vector();

        for (int i = 0; i < getChildren().size(); i++)
            v.add(getChildren().get(i));

        return v.elements();
    }

    public Object clone() {
        return this;
    }

    /**
     * Remove a child from this component, will set parent to be null.
     *
     * @param child : BaseComponent child to be removed.
     */
    public void removeChild(TreeNodeBase child) {
        synchronized (children) {
            children.remove(child);
        }

        child.setParent(null);
    }

    /**
     * Remove all children from its child list.
     */
    public void removeChildren() {
        for (int i = 0; i < getChildren().size(); i++) {
            ((TreeNodeBase) getChildren().get(i)).setParent(null);
        }
        children.clear();
    }

    //when graphic value changed
    public Object valueChanged(Object object) {
        return object;
    }

    /**
     * Returns the type of this component. By default, it returns its class name.
     * Subclass can provide a component type system, and override this method to
     * manage type related APIs.
     *
     * @return return-value
     */
    protected String getComponentType() {
        return getClass().getName();
    }

    /**
     * to be removed This method does not guarantee this instance is added to the
     * children list of the parent component. This method should not be called
     * directly.  addChild calls this to change the child's parent.
     *
     * @param parent : BaseComponent
     */
    private void setParent(TreeNodeBase parent) {
        this.parent = parent;
    }
}
